package terminal.communication;

import org.jboss.remoting.CannotConnectException;
import org.jboss.remoting.ServerInvoker.InvalidStateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import terminal.gui.MainWindow;
import core.communication.message.Message;

@Component
@Scope("prototype")
public class MessageSender implements ApplicationContextAware, Runnable {

	private static final Logger L = LoggerFactory.getLogger(MessageSender.class);

	private ApplicationContext context;

	private volatile Thread thread = null;
	private CommunicationService communicationService;
	private MainWindow mainWindow;

	public MessageSender() {
		L.debug("Tworzę MessageSender");

		thread = new Thread(this, "sender");

		L.debug("Utworzono MessageSender");
	}

	public void stop() {
		L.debug("Stop: {} {}", thread.getName(), thread.getId());

		thread = null;
		wakeUp();
	}

	public synchronized void wakeUp() {
		L.debug("Wstaje: MessageSender");

		notify();
	}
	
	public void setMainWindow(MainWindow mainWindow) {
		this.mainWindow = mainWindow;
	}

	@Override
	public void run() {
		communicationService = context.getBean(CommunicationService.class);
		Message message = null;
		Client client = null;
		while (thread != null) {
			message = null;
			client = null;
			// Pobieramy wiadomość do wysłania
			L.debug("Pobieram wiadomość");
			message = communicationService.getMessageToSend();
			// Jeżeli nie ma wiadomości to czekamy
			if (message == null) {
				synchronized (this) {
					try {
						if (thread == null)
							return;
						L.debug("Nie ma wiadomości, usypiam");
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			} else {
				L.debug("Wysyłam " + message.toString());
				client = communicationService.getRKWClient();
				L.debug("Pobrano klienta: {}", client.toString());
				try {
					L.debug("Próbuje wysłać " + message.toString());
					Boolean result = client.send(message);
					if (mainWindow != null) {
					if (result)
						mainWindow.displayInformationMessage("Vote information", "Vote was successfully sent");
					else
						mainWindow.displayErrorMessage("Vote error", "Invalid voters token");
					}
					
					communicationService.removeMessageToSend(message);
					// Nie możemy połączyć się z klientem
				} catch (CannotConnectException | InvalidStateException e) {
					L.debug("Brak połączenia z klientem " + client.toString());
					if (thread == null)
						return;
					if (mainWindow != null)
						mainWindow.displayErrorMessage("Communication error", "Could not connect to RKW.");
					communicationService.resendMessage(message);
				} catch (Throwable e) {
					if (thread == null)
						return;
					e.printStackTrace();
				}
			}
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		context = applicationContext;
	}

}
